/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.imgcog;

/**
 *
 * @author Mitch
 */
public class SupervisedNet extends Net implements ErrorProvider, ErrorListener {
    private boolean firstErrorDirty = true;
    private ErrorListener errorListener;
    
    @Override
    public boolean addListener(ErrorListener listener) {
        if(errorListener == null && listener.isCompatible(this)) {
            errorListener = listener;
            return true;
        }
        return false;
    }
    
    @Override
    public boolean removeListener(ErrorListener listener) {
        if(errorListener == listener) {
            errorListener = null;
            return true;
        }
        return false;
    }

    @Override
    public float[] getError() {
        if(firstErrorDirty) {
            firstErrorDirty = false;
            getRunner().get(getRunner().firstLayerError);
        }
        return getRunner().firstLayerError;
    }
    
    
    @Override
    protected SupervisedNetRunner getRunner() {
        return (SupervisedNetRunner) super.getRunner();
    }


    @Override
    public void errorUpdateEvent(ErrorProvider ep) {
        if(setError(ep)) {
            backprop();
        }
        errorListener.errorUpdateEvent(this);
    }
    
    protected void backprop() {
        //TODO
    }
    
    protected boolean setError(ErrorProvider ep) {
        float[] src = ep.getError();
        if (src.length == getRunner().lastLayerError.length) {
            System.arraycopy(src, 0, getRunner().lastLayerError, 0, src.length);
            getRunner().put(getRunner().lastLayerError);
            return true;
        }
        return false;
    }

    @Override
    public int getErrorLength() {
        return getRunner().firstLayerError.length;
    }

    @Override
    public boolean isCompatible(ErrorProvider ep) {
        return getRunner().lastLayerError.length == ep.getErrorLength();
    }
    
    
    protected abstract class SupervisedNetRunner extends UntrainedNetRunner {

        private final float[] firstLayerError;
        private final float[] lastLayerError;
        private final float[] hiddenLayerError;
        //
        private final float[] dw;
        private final float[] dwa;
        private final float[] mu;

        public SupervisedNetRunner(NetConfig conf) {
            super(conf);
            firstLayerError = new float[getLayerSize(0)];
            lastLayerError = new float[getLayerSize(layerCount - 1)];
            hiddenLayerError = new float[conf.getTotalNodeCount() - 
                    (firstLayerError.length + lastLayerError.length)];
            dw = new float[conf.getTotalWeightCount()];
            dwa = new float[conf.getTotalWeightCount()];
            mu = new float[conf.getTotalWeightCount()];
        }
        
        

        protected final float getNodeError(int layer, int node) {
            if (layer == 0) {
                return firstLayerError[node];
            }
            if (layer == layerCount - 1) {
                return lastLayerError[node];
            }
            if (layer < layerCount - 1) {
                return hiddenLayerError[getHiddenStart(layer) + node];
            }
            return 0;
        }

        protected final void setNodeError(int layer, int node, float val) {
            if (layer == 0) {
                firstLayerError[node] = val;
            }
            if (layer == layerCount - 1) {
                lastLayerError[node] = val;
            }
            if (layer < layerCount - 1) {
                hiddenLayerError[getHiddenStart(layer) + node] = val;
            }
        }

        protected final void addNodeError(int layer, int node, float val) {
            if (layer == 0) {
                firstLayerError[node] = +val;
            }
            if (layer == layerCount - 1) {
                lastLayerError[node] = val;
            }
            if (layer < layerCount - 1) {
                hiddenLayerError[getHiddenStart(layer) + node] = val;
            }
        }

        protected float getDW(int index) {
            return dw[index];
        }

        protected float getDWA(int index) {
            return dwa[index];
        }

        protected float getMu(int index) {
            return mu[index];
        }

        protected void setDW(int index, float val) {
            dw[index] = val;
        }

        protected void setDWA(int index, float val) {
            dwa[index] = val;
        }

        protected void setMu(int index, float val) {
            mu[index] = val;
        }

        protected void addDW(int index, float val) {
            dw[index] += val;
        }

        protected void addDWA(int index, float val) {
            dwa[index] += val;
        }

        protected void addMu(int index, float val) {
            mu[index] += val;
        }
    }
}
